package cn.qing.aspect;

import cn.qing.pojo.SysLog;
import cn.qing.service.SysLogService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.User;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Date;

@Component
@Aspect
public class SysLogAspect {
    //    @Autowired
    @Resource
    HttpServletRequest request;
    @Autowired
    SysLogService sysLogService;

    private Date visitTime; // 访问时间
    private Class<?> clazz; // 访问的Class对象
    private Method method; // 访问的Method对象

    /**
     * 前置通知
     *  获取访问时间，获取访问的是哪个类的哪个方法
     * @param joinPoint 连接点对象
     */
    @Before("execution(* cn.qing.controller.*.*(..))")
    public void before(JoinPoint joinPoint) throws NoSuchMethodException {
        // 访问时间
        visitTime = new Date();
        // 访问的目标类
        clazz = joinPoint.getTarget().getClass();
        // 访问的目标方法
        String methodName = joinPoint.getSignature().getName();
        // 获取目标方法的参数
        Object[] args = joinPoint.getArgs();
        // 判断是否有参数
        if (args != null && args.length > 0) {
            // 组装Class类型的数组
            Class<?>[] argsClass = new Class[args.length]; // 数组的长度就是参数的个数
            // 将args数组中的参数放入Class类型的数组(argsClass)中
            for (int i = 0; i < args.length; i++) {
                argsClass[i] = args[i].getClass();
            }
            // 获取有参数的方法对象
            method = clazz.getMethod(methodName, argsClass);
        } else {
            // 获取无参数的方法对象
            method = clazz.getMethod(methodName);
        }
    }

    /**
     * 最终通知
     *  获取方法的执行时长，获取访问的URL，获取访问的IP，获取操作的用户名
     */
    @After("execution(* cn.qing.controller.*.*(..))")
    public void after(){
        // 访问时长 单位:毫秒
        Long time = new Date().getTime() - visitTime.getTime();
        // 访问的URL：@RequestMapping("/order") + @RequestMapping("/findAll") 如：/order/findAll
        String url = "";
        if (clazz != null && method != null) {
            // 1.获取类上映射的路径
            String classUrl = null;
            RequestMapping clazzAnno = clazz.getAnnotation(RequestMapping.class);
            if (clazzAnno != null) {
                classUrl = clazzAnno.value()[0];
            }
            // 2.获取方法上映射的路径
            String methodUrl = null;
            RequestMapping methodAnno = method.getAnnotation(RequestMapping.class);
            if (methodAnno != null) {
                methodUrl = methodAnno.value()[0];
            }
            // 3.拼接最终的url
            url = classUrl + methodUrl;
        }
        // 获取访问的IP
        String ip = request.getRemoteAddr();
        // 获取访问的用户
        // 1.获取SecurityContext上下文对象
        SecurityContext context = SecurityContextHolder.getContext();
        // 2.通过上下文对象获取登录的用户对象
        User user = (User) context.getAuthentication().getPrincipal();
        // 3.获取用户名
        String username = user.getUsername();
        // 将上述获取到的数据组装到SysLog对象中，调用service的方法，保存到数据库中
        SysLog sysLog = new SysLog();
        sysLog.setVisitTime(visitTime);
        sysLog.setUsername(username);
        sysLog.setIp(ip);
        sysLog.setUrl(url);
        sysLog.setExecutionTime(time);
        sysLog.setMethod(clazz.getName() + "." + method.getName());
        // 保存
        sysLogService.save(sysLog);
    }
}
